<?php

// Database 操作类，加载了外部的数据库驱动类

class Dbop{

    public $db = NULL; // 当前数据库操作对象
    public $config = array();
    public $driver = '';
    public $sql = ''; // sql语句，主要用于输出构造成的sql语句
    public $pre = ''; // 表前缀，主要用于在其他地方获取表前缀
    public $suf = ''; // 表后缀，主要用于在其他地方获取表后缀
    public $data = array(); // 数据信息
    static $uobj = array(); // 实例化的前数据对象

    function __construct($config=array()){
        $this->config = empty($config) ? self::getCfg() : $config;
        $this->driver = $this->config['db_driver'];
        $this->pre = $this->config['db_pre'];
        $this->suf = $this->config['db_suf']; //dump($this->suf, $this->config); 
    }

    // 连接数据库
    function connect(){ 
        if(!is_object($this->db)){ //$this->db不是对象，则连接数据库
            $file = 'db'.ucfirst($this->driver);
            require_once DIR_YSKIT."/driver/{$file}.php";
            $class = $file; //"\\yskit\\$file";
            $this->db = new $class();//连接数据库
            $this->db->connect($this->config); 
        }
    }

    // 执行原生sql语句，如果sql是查询语句，返回二维数组
    function get($sql, $params=[], $mode=0){ 
        //判断当前的sql是否是查询语句
        $sql = $this->sqlJoin($sql, $params);
        if(strpos(trim(strtolower($sql)),'select')===0){ 
            $data = $this->db->get($sql, $mode);
            return $data;
        }else{ //不是查询条件，执行之后，直接返回
            $res = $this->run($sql, $params);
            return $res;
        }
    }

    // 执行sql语句
    function run($sql, $params=[]){ 
        $sql = $this->sqlJoin($sql, $params);
        $re = $this->db->run($sql);
        return $re;
    }

    // 返回sql语句
    function getSql(){
        return $this->sql;
    }
    // 返回quoteSql语句
    function quoteSql($sql){
        return $this->db->quoteSql($sql);
    }

    // 防注入
    function sqlJoin($sql, $params=[]){ 
        $sql = preg_replace_callback("/ \{(\w+)\} /", function($itms){ 
            return " `{$this->pre}{$itms[1]}{$this->suf}` ";
        }, $sql); 
        if(!empty($params)){
            foreach($params as $pk=>$pv){
                $params[$pk] = is_array($pv) ? $pv : $this->db->quoteSql($pv); 
            } 
            $sql = vsprintf($sql, $params);
        }
        $this->sql = $sql;
        return $sql;
    }

    # =============================

    // 获取db实例
    static function db($config=[]){ 
        $dbcfg = self::getCfg(); 
        if(empty($config)){
            $key = 'db0_main';
            $config = $dbcfg;
        }elseif(is_string($config)){ // `user`
            $key = $config;
            $config = isset($dbcfg[$key]) ? $dbcfg[$key] : $dbcfg;
            $key = isset($dbcfg[$key]) ? "db1_$key" : 'db0_main';
        }else{ // array()
            $config = array_merge($dbcfg, $config);
            $key = 'dba_'.substr(md5(json_encode($config)),0,16); 
        }
        if(empty(self::$uobj[$key])){
            self::$uobj[$key] = new self($config); 
            self::$uobj[$key]->connect(); 
        }
        return self::$uobj[$key];
    }
    // 获取db配置
    static function getCfg($key=''){ 
        static $dbcfg;
        if(empty($dbcfg)){
            require DIR_APP.'/config/dbcfg.php';
            $dbcfg = $_cfgs;
        }
        return ($key && isset($dbcfg[$key])) ? $dbcfg[$key] : $dbcfg;
    }

    # =============================

    // 分页信息
    static function pager($prec, $page=1, $psize=10, $url=1){
        $pmax = (empty($prec) || empty($psize)) ? 1 :  intval(ceil($prec/$psize));
        $data = ['prec'=>$prec, 'page'=>$page, 'psize'=>$psize, 'pmax'=>$pmax];  
        $data['first'] = 1;
        $data['prev'] = max(1, $page-1);
        $data['next'] = min($pmax, $page+1);
        $data['last'] = $pmax;
        // 
        if($url){
            $url = strlen($url)>6 ? $url : uval('REQUEST_URI','','s');
            $url = preg_replace("/(\?|\&)page\=(\d+)/", "$1page={page}", $url); 
            if(!strpos($url,'page={page}')){ $url .= (strpos($url,'?')?'&':'?')."page={page}"; } 
            $tab = ['first','prev','next','last'];
            foreach($tab as $tk){
                $data["_$tk"] = str_replace('{page}', $data[$tk], $url);
            }
        }
        return $data;
    }
    /*
        data($mod, $whr='', $lmt='10', $ord='');
        $mod: info, info.join
        $whr: `show`='all', `show`='0', 
        $lmt: 1, 10, 3,10, 10.page, count
        $ord: atime-1, atime
    */
    // 模型数据
    static function data($mod, $whr='', $lmt='10', $ord='', $ops=[]){
        $dbk = (is_array($ops) && isset($ops['dbk'])) ? $ops['dbk'] : ((!empty($ops) && is_string($ops)) ? $ops : '');
        $db = self::db($dbk);
        $cfg = self::imod($mod); self::iwhr($cfg, $whr); self::ilmt($cfg, $lmt); 
        $cfg['ord'] = $ord ? $ord : $cfg['ord']; //dump($cfg);
        $fields = empty($ops['fields']) ? '*' : $ops['fields'];
        $sfrom = "SELECT $fields FROM {{$cfg['tab']}} "; 
        $where = empty($cfg['whr']) ? '' : 'WHERE '.$cfg['whr'];
        // data
        if($cfg['mode']=='count'){
            $res = $db->get("SELECT COUNT(*) FROM {{$cfg['tab']}} $where", [], 2); 
            return $res;
        }elseif($cfg['mode']=='page'){
            $page = intval(uval('page',1)); if($page<1){ $page=1; }
            $prec = intval(uval('prec',-1)); 
            $psize = intval($lmt); if($psize<1){ $psize=10; } 
            if($prec<0){ 
                $prec = $db->get("SELECT COUNT(*) FROM {{$cfg['tab']}} $where", [], 2); 
            } 
            $pager = self::pager($prec, $page, $psize); //
            $start = ($page-1)*$psize; $limit = " LIMIT $start,$psize "; 
            $sql = "$sfrom $where ORDER BY $cfg[ord] $limit"; 
            $res = $db->get($sql, [], 0); //dump($pager);
        }else{
            $limit = ' LIMIT ' . ($cfg['mode']==1 ? '1' : $cfg['lmt']); 
            $sql = "$sfrom $where ORDER BY $cfg[ord] $limit"; //dump($sql);
            $res = $db->get($sql, [], 0); 
        } 
        // join
        if(!empty($cfg['join'])) self::dextJoin($res, $cfg);
        if($cfg['mode']==1 && !empty($res)) $res = $res[0];
        if($cfg['mode']=='page' && !empty($res)) $res = ['lists'=>$res, 'pager'=>$pager];
        return $res ?: [];
    }

    // lmt: 1, 10, 3,10, 10.page, count
    static function ilmt(&$cfg, $lmt){
        if($lmt=='count'){
            $cfg['lmt'] = '';
            $cfg['mode'] = 'count';
        }elseif(strpos($lmt,'.')){
            $cfg['lmt'] = intval($lmt);
            $cfg['mode'] = 'page';
        }else{
            $cfg['lmt'] = $lmt;
            $cfg['mode'] = intval($lmt)==1 ? '1' : 'list';
        }
    }
    // whr: `show`='all'
    static function iwhr(&$cfg, $whr){
        $whr = $whr ? $whr : '';
        if(in_array($cfg['pid'],array('docs','users','coms','advs'))){
            if(strstr($whr,"`show`='all'")){
                $whr = str_replace([" AND `show`='all'","`show`='all' AND ","`show`='all'"],'',$whr);
            }elseif(strstr($whr,'`show`')){ 
                // 
            }else{ //if(!strstr($whr,'`show`=')){ 
                $whr .= " AND (`show`='1')";
            } 
        }
        if(substr($whr,0,5)==' AND ') $whr = substr($whr,5);
        $cfg['whr'] = $whr;
    }
    // mod: info, info.join
    static function imod($mod){
        $sy_mods = read('sy_mods'); 
        $tmp = explode('.', $mod); $mod = $tmp[0];
        if(isset($sy_mods[$tmp[0]])){
            $cfg = $sy_mods[$tmp[0]];
        }else{
            abort("Module `$tmp[0]` NOT Found.", 602);
        }
        if(empty($cfg['etab']) || empty($tmp[1])){
            $join = 0; $jtab = '';
        }else{
            $join = 1; $jtab = "dext_$mod";
        }
        // infos
        if($cfg['pid']=='docs'){
            $tab = 'docs_'.$mod;  $fpk = 'did'; $ord = 'did DESC'; 
        }elseif($cfg['pid']=='users'){
            $tab = 'users_'.$mod; $fpk = 'uid'; $ord = 'atime DESC'; 
        }elseif($cfg['pid']=='advs'){
            $tab = 'advs_'.$mod;  $fpk = 'aid'; $ord = 'atime DESC'; 
        }elseif($cfg['pid']=='coms'){    
            $tab = 'coms_'.$mod;  $fpk = 'cid'; $ord = 'cid DESC';
        }elseif($cfg['pid']=='types'){    
            $tab = 'types_'.$mod; $fpk = 'kid'; $ord = 'kid  DESC'; //不使用 
        }else{
            $tab = empty($cfg['pid']) ? $mod : $cfg['pid'].$mod;
            $fpk = empty($cfg['fpk']) ? 'kid' : $cfg['fpk']; $ord = empty($cfg['ord']) ? "$fpk DESC" : $cfg['ord']; 
        }
        foreach(['mod','tab','join','jtab','ord','fpk'] as $k0){
            $cfg[$k0] = $$k0;
        } //dump($cfg);
        return $cfg;
    }
    // 附加扩展表信息
    static function dextJoin(&$re, $cfg){
        $fpk = $cfg['fpk']; $ids = ''; $re2 = [];
        foreach($re as $k=>$v){
            $ids .= (empty($ids) ? '' : ',')."'".$v[$fpk]."'";
        } 
        if(empty($ids)) return;
        $sql = "SELECT * FROM {{$cfg['jtab']}} WHERE $fpk IN($ids)"; 
        $re1 = db()->get($sql, [], 0); 
        // 
        foreach($re1 as $k1=>$v1){ 
            $re2[$v1[$fpk]] = $v1;
        } 
        foreach($re as $k=>$v){
            if(isset($re2[$v[$fpk]])){
                $re[$k] = $v + $re2[$v[$fpk]];
            }
        }
    }

    // 设置表名称(处理前后缀)，$nofix为true的时候，不加上默认的表前缀
    function table($table, $nofix=false){
        $full = "`{$this->pre}{$itms[1]}{$this->suf}`";
        if($nofix===2){ 
            return $full;
        }elseif($nofix){
            return '`'.$table.'`';
        }else{
            $otps['bare_tab'] = $table;
            $otps['table'] = $full;
            return $otps;
        }
    }

}


